package IC.AST;

import IC.AST.nodes.*;

/** 
 * A default propogating visitor abstraction
 * It walk on the AST and call a method on each node
 * useful for doing smth on all nodes or overriding and
 * being able to skip not interesting nodes 
 * @author root
 *
 * @param <D>
 * @param <U>
 */
public class DefaultVisitor<D,U> implements PropogatingVisitor<D,U> {

	/**
	 * called before children are processed
	 * @param node the node
	 * @param d the down-object
	 */
	public void processPre(ASTNode node, D d) {
	}

	/**
	 * called after chuldren are processed
	 * @param node the node
	 * @param d the down-object
	 * @return the up return
	 */
	public U processPost(ASTNode node, D d) {
		return null;
	}

	public U visit(Program that, D d) throws Exception {
		processPre(that,d);
		for (ClassDecl cls: that.getClasses()) {
			cls.accept(this,d);
		}
		return processPost(that,d);
	}

	public U visit(ArrayTypeDecl that, D d) throws Exception {
		processPre(that,d);
		that.getElemType().accept(this,d);
		return processPost(that,d);
	}

	public U visit(ClassDecl that, D d) throws Exception {
		processPre(that,d);
		for (ASTNode cmd : that.getClassMembers()) {
			cmd.accept(this,d);
		}
		return processPost(that,d);
	}

	public U visit(ClassTypeDecl that, D d) throws Exception {
		processPre(that,d);
		return processPost(that,d);
	}

	public U visit(FieldsDecl that, D d) throws Exception {
		processPre(that,d);
		that.getType().accept(this,d);
		return processPost(that,d);
	}

	public U visit(FormalDecl that, D d) throws Exception {
		processPre(that,d);
		that.getType().accept(this,d);
		return processPost(that,d);
	}

	public U visit(MethodDecl that, D d) throws Exception {
		processPre(that,d);
		if(that.getReturnType() != null) {
			that.getReturnType().accept(this,d);
		}
		for(FormalDecl fd : that.getFormals()) {
			fd.accept(this,d);
		}
		if(that.getBody() != null) {
			that.getBody().accept(this,d);
		}
		return processPost(that,d);
	}

	public U visit(PrimitiveTypeDecl that, D d) throws Exception {
		processPre(that,d);
		return processPost(that,d);
	}

	public U visit(ArrayElmVar that, D d) throws Exception {
		processPre(that,d);
		that.getArray().accept(this,d);
		that.getIndex().accept(this,d);
		return processPost(that,d);
	}

	public U visit(BinaryExpr that, D d) throws Exception {
		processPre(that,d);
		that.getLeft().accept(this,d);
		that.getRight().accept(this,d);
		return processPost(that,d);
	}

	public U visit(BoolLiteral that, D d) throws Exception {
		processPre(that,d);
		return processPost(that,d);
	}

	public U visit(StaticCall that, D d) throws Exception {
		processPre(that,d);
		for ( Expression arg : that.getArgs()) {
			arg.accept(this,d);
		}
		return processPost(that,d);
	}

	public U visit(VirtualCall that, D d) throws Exception {
		processPre(that,d);
		if (that.getInstance() != null) {
			that.getInstance().accept(this,d);
		}
		for ( Expression arg : that.getArgs()) {
			arg.accept(this,d);
		}
		return processPost(that,d);
	}

	public U visit(IntLiteral that, D d) throws Exception {
		processPre(that,d);
		return processPost(that,d);
	}

	public U visit(LengthExpr that, D d) throws Exception {
		processPre(that,d);
		that.getArray().accept(this,d);
		return processPost(that,d);
	}

	public U visit(SimpleVar that, D d) throws Exception {
		processPre(that,d);
		return processPost(that,d);
	}

	public U visit(MemberVar that, D d) throws Exception {
		processPre(that,d);
		that.getInstance().accept(this,d);
		return processPost(that,d);
	}

	public U visit(NewArrayExpr that, D d) throws Exception {
		processPre(that,d);
		that.getElemType().accept(this,d);
		that.getSize().accept(this,d);
		return processPost(that,d);
	}

	public U visit(NewClassExpr that, D d) throws Exception {
		processPre(that,d);
		return processPost(that,d);
	}

	public U visit(NullLiteral that, D d) throws Exception {
		processPre(that,d);
		return processPost(that,d);
	}

	public U visit(StringLiteral that, D d) throws Exception {
		processPre(that,d);
		return processPost(that,d);
	}

	public U visit(ThisExpr that, D d) throws Exception {
		processPre(that,d);
		return processPost(that,d);
	}

	public U visit(UnaryExpr that, D d) throws Exception {
		processPre(that,d);
		that.getOperand().accept(this,d);
		return processPost(that,d);
	}

	public U visit(AssignStmt that, D d) throws Exception {
		processPre(that,d);
		that.getSrc().accept(this,d);
		that.getDest().accept(this,d);
		return processPost(that,d);
	}

	public U visit(StmtBlock that, D d) throws Exception {
		processPre(that,d);
		for(Statement stmt : that.getStmts()) {
			stmt.accept(this,d);
		}
		return processPost(that,d);
	}

	public U visit(BreakStmt that, D d) throws Exception {
		processPre(that,d);
		return processPost(that,d);
	}

	public U visit(ContinueStmt that, D d) throws Exception {
		processPre(that,d);
		return processPost(that,d);
	}

	public U visit(IfStmt that, D d) throws Exception {
		processPre(that,d);
		that.getCondition().accept(this,d);
		that.getIfStmt().accept(this,d);
		if (that.getElseStmt() != null) {
			that.getElseStmt().accept(this,d);
		}
		return processPost(that,d);
	}

	public U visit(ReturnStmt that, D d) throws Exception {
		processPre(that,d);
		if(that.getExpr() != null) {
			that.getExpr().accept(this,d);
		}
		return processPost(that,d);
	}

	public U visit(VarDeclStmt that, D d) throws Exception {
		processPre(that,d);
		that.getType().accept(this,d);
		that.getVar().accept(this, d);
		if (that.getInit() != null) {
			that.getInit().accept(this,d);
		}
		return processPost(that,d);
	}

	public U visit(WhileStmt that, D d) throws Exception {
		processPre(that,d);
		that.getCondition().accept(this,d);
		that.getBody().accept(this,d);
		return processPost(that,d);
	}

	public U visit(MethodCallStmt that, D d) throws Exception {
		processPre(that,d);
		that.getCall().accept(this,d);
		return processPost(that,d);
	}
}